/*
 * forwardchannel.cpp
 *
 *  Created on: 2013-6-5
 *      Author: dylan
 */

#include "forwardchannel.h"
#include <cstring>
#include <cstdio>

CForwardChannel::CForwardChannel(){
	m_income = NULL;
	m_outcome = NULL;
	m_thread = NULL;

	m_addr = NULL;
	m_timeout = 0;
}

CForwardChannel::~CForwardChannel(){
	if( m_income!=NULL )
		delete m_income;

	if( m_outcome!=NULL )
		delete m_outcome;

	if( m_thread!=NULL ){
		m_thread->cancel();
		delete m_thread;
	}

	if( m_addr!=NULL )
		delete  m_addr;
}

bool CForwardChannel::setIncome( CIoDevice* io ){
	if( ifStart() )
		return false;

	if( m_income!=NULL )
		delete m_income;

	m_income = io;

	return true;
}

bool CForwardChannel::setOutcomeAddr( const CIoAddress& addr ){
	if( m_addr==NULL )
		m_addr = new CIoAddress;

	*m_addr = addr;

	if( m_addr->type()==CIoAddress::EIO_Unknow )
		return false;
	return true;
}

bool CForwardChannel::setTimeout( const long& sec ){
	m_timeout = sec;
	return true;
}

static void* execFCRun( void* arg ){
	printf("--execFCRun\n");
	CForwardChannel* fc = (CForwardChannel*)arg;
	fc->run();
	printf("execFCRun--\n");
	return NULL;
}

bool CForwardChannel::start(){
	printf("CForwardChannel::start()\n");
	if( ifStart() || m_income==NULL )
		return false;

	if( m_addr==NULL  )
		return false;

	if( m_thread==NULL )
		m_thread = new CSysThread();

	m_thread->exec(execFCRun,this);
	m_thread->detach();

	return true;
}


bool CForwardChannel::stop(){
	if( ifStart() ){
		m_thread->cancel();
		return true;
	}

	return false;
}

bool CForwardChannel::ifStart(){
	if( m_thread==NULL )
		return false;
	if( m_thread->ifRunning() )
		return true;

	return false;
}

void CForwardChannel::run(){
	if( !connectOutcome() ){
		fprintf(stderr,"can not connect to outcome:CForwardChannel::run\n");
		delete m_income;
		m_income = NULL;
		return;
	}

	CIoSelector selector;

	for(;;){
		selector.clear();
		selector.addIo(m_income);
		selector.addIo(m_outcome);

		printf("CForwardChannel::run()[ select timeout=%ld ]\n",m_timeout);
		if( !selector.selectIo(m_timeout) ){
			printf("CForwardChannel::run()[select time out]\n");
			delete m_income;
			delete m_outcome;

			m_income = NULL;
			m_outcome = NULL;
			return;
		}
		printf("CForwardChannel::run()[select->ok]\n");

		if( selector.ifIoReady(m_income) ){
			printf("CForwardChannel::run()[ income ready ]\n");
			if( 0>=readWrite(m_income,m_outcome) ){
				printf("CForwardChannel::run()[income->outcome fail]\n");
				delete m_income;
				delete m_outcome;

				m_income = NULL;
				m_outcome = NULL;
				return;
			}
		}

		if( selector.ifIoReady(m_outcome) ){
			printf("CForwardChannel::run()[ outcome ready ]\n");
			if( 0>=readWrite(m_outcome,m_income) ){
				printf("CForwardChannel::run()[outcome->income fail]\n");
				delete m_income;
				delete m_outcome;

				m_income = NULL;
				m_outcome = NULL;
				return;
			}
		}
	}
}

bool CForwardChannel::connectOutcome(){
	if( m_addr==NULL )
		return false;

	if( m_outcome!=NULL )
		delete m_outcome;
	m_outcome = NULL;

	switch( m_addr->type() ){
	case CIoAddress::EIO_Tcp:
	{
		CTcpClient tcp;
		m_outcome = tcp.connectToHost(m_addr->host(),m_addr->port());
		if( m_outcome==NULL )
			return false;
	}
		break;
	case CIoAddress::EIO_Udp:
	{
		CUdpDevice* udp = new CUdpDevice;
		udp->setPeerAddress(m_addr->host(),m_addr->port());
		udp->openDevice(NULL,0);

		m_outcome = udp;
	}
		break;
	case CIoAddress::EIO_Com:
	{
		CComDevice* com = new CComDevice;
		if( !com->openDevice(m_addr->device()) || !com->setOpt(m_addr->bps(),8,m_addr->cs(),m_addr->stopBit()) ){
			delete com;
			return false;
		}
		m_outcome = com;
	}
		break;
	default:
		return false;
	}

	return true;
}

bool CForwardChannel::readWrite( CIoDevice* read,CIoDevice* write ){
	unsigned char buf[1024];
	int size;

	printf("CForwardChannel::readWrite()\n");
	size = read->readData(buf,1024);
	printf("CForwardChannel::readWrite()[read:%d]\n",size);
	if( size<=0 ){
		return false;
	}

	if( size!=write->writeData(buf,size) ){
		printf("CForwardChannel::readWrite()[write]\n");
		return false;
	}

	printf("CForwardChannel::readWrite():ok\n");
	return true;
}
